# 继承是面向对象的三大特征之一，也是实现代码复用的重要手段。继承经常用于创建和现有类功能类似的新类，又或是新类只需要在现有类基础上添加一些成员（属性和方法），但又不想直接将现有类代码
# 复制给新类。
#
# 例如，有一个 Shape 类，该类的 draw() 方法可以在屏幕上画出指定的形状，现在需要创建一个 Rectangle 类，要求此类不但可以在屏幕上画出指定的形状，还可以计算出所画形状的面积。要创建这
# 样的 Rectangle 类，除了将 draw() 方法直接复制到新类中，并添加计算面积的方法，其实还有更简单的方法，即让 Rectangle 类继承 Shape 类，这样当 Rectangle 类对象调用 draw() 方法时，
# Python 解释器会自动去 Shape 类中调用该方法，如此，我们只需在 Rectangle 类中添加计算面积的方法即可。
#
# Python 中，实现继承的类称为子类，被继承的类称为父类（也可称为基类、超类）。子类继承父类的语法是：在定义子类时，将多个父类放在子类之后的圆括号里。语法格式如下：
#
#   class 类名(父类1, 父类2, ...)：
#         #类定义部分
#
# 注意，Python 的继承是多继承机制，即一个子类可以同时拥有多个直接父类。
#
# 从上面的语法格式来看，定义子类的语法非常简单，只需在原来的类定义后增加圆括号，并在圆括号中添加多个父类，即可表明该子类继承了这些父类。如果在定义一个 Python 类时，并未显式指定这个类
# 的直接父类，则这个类默认继承 object 类。
#
#   object 类是所有类的父类，要么是直接父类，要么是间接父类。
#
# 父类和子类的关系，就好像水果和苹果的关系，苹果是一种特殊的水果，苹果是水果的子类，水果是苹果的父类，因此可以说，苹果继承了水果。不仅如此，由于子类是一种特殊的父类，因此父类包含的范围
# 总比子类包含的范围要大，所以可以认为父类是大类，而子类是小类。
#
# 从实际意义上看，子类是对父类的扩展，子类是一种特殊的父类。从这个意义上看，使用继承来描述子类和父类的关系是错误的，用扩展更恰当。因此，这样的说法更加准确：苹果扩展了水果这个类。
#
#   从子类的角度来看，子类扩展（extend）了父类；但从父类的角度来看，父类派生（derive）出子类。也就是说，扩展和派生所描述的是同一个动作，只是观察角度不同而已。
class Fruit:
    def info(self):
        print("我是一个水果！重%g克" % self.weight)


class Food:
    def taste(self):
        print("不同食物的口感不同")


# 定义Apple类，继承了Fruit和Food类
class Apple(Fruit, Food):
    pass


# 创建Apple对象
a = Apple()
a.weight = 5.6
# 调用Apple对象的info()方法
a.info()
# 调用Apple对象的taste()方法
a.taste()


# 关于Python的多继承
#
# 大部分面向对象的编程语言（除了 C++）都只支持单继承，而不支持多继承，这是由于多继承不仅增加了编程的复杂度，而且很容易导致一些莫名的错误。
#
# Python 虽然在语法上明确支持多继承，但通常推荐如果不是很有必要，则尽量不要使用多继承，而是使用单继承，这样可以保证编程思路更清晰，而且可以避免很多麻烦。
#
# 当一个子类有多个直接父类时，该子类会继承得到所有父类的方法，这一点在前面示例中己经做了示范。现在的问题是，如果多个父类中包含了同名的方法，此时会发生什么呢？此时排在前面的父类中的方法会“遮蔽”
# 排在后面的父类中的同名方法。例如如下代码：
class Item:
    def info(self):
        print("Item中方法:", '这是一个商品')


class Product:
    def info(self):
        print("Product中方法:", '这是一个工业产品')


# Mouse 继承了 Item 类和 Product 类，由于 Item 排在前面，因此 Item 中定义的方法优先级更好，Python 会优先到 Item 父类中搜寻方法，一旦在 Item 父类中搜寻到目标方法，Python 就不会
# 继续向下搜寻了。
class Mouse(Item, Product):
    pass


m = Mouse()
m.info()
